package haust;

import static org.lwjgl.openal.AL10.*;
import static org.lwjgl.util.glu.GLU.*;
import org.lwjgl.input.Keyboard;

import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils;

public class Camera
{
	private Vec3 movement;
	private Vec3 cross;
	public Vec3 pos;
	public Vec3 view;
	public Vec3 up;
	
	private FloatBuffer listenerPos;
	private FloatBuffer listenerVel;
	private FloatBuffer listenerDir;
	
	/**
	 * Initializes variables to the following values:
	 * position: (0,1,0)
	 * view: (0,0,-1)
	 * up: (0,1,0)
	 */
	public Camera()
	{
		pos = new Vec3(0,1,0);
		view = new Vec3(0,0,-1);
		up = new Vec3(0,1,0);
		cross = new Vec3();
		movement = new Vec3();
		
		
		listenerPos = BufferUtils.createFloatBuffer(3);
		listenerVel = BufferUtils.createFloatBuffer(3);
		listenerDir = BufferUtils.createFloatBuffer(6);
		
		//Position
		listenerPos.put( new float[] {0.0f, 1.0f, 0.0f} ).rewind();

		//Velocity
		listenerVel.put( new float[] {0.0f, 0.0f, 0.0f} ).rewind();

		// Direction (3 values) and Up (3 values)
		listenerDir.put( new float[] {0.0f, 0.0f, -1.0f,  0.0f, 1.0f, 0.0f} ).rewind();
	}
	
	/**
	 * Updates the listener variables and sends Position, Velocity and Orientation to OpenAL
	 */
	private void updateListener()
	{
		listenerPos.put(0, pos.getX() );
		listenerPos.put(1, pos.getY() );
		listenerPos.put(2, pos.getZ() );
		
		listenerDir.put( 0, view.getX() );
		listenerDir.put( 1, view.getY() );
		listenerDir.put( 2, view.getZ() );
		
		alListener(AL_POSITION, listenerPos);
		alListener(AL_VELOCITY, listenerVel);
		alListener(AL_ORIENTATION, listenerDir);
	}
	
	/**
	 * Checks for movement (forward, backward, left or right).
	 * Moves in relation to the orientation of the camera.
	 * Utilizes org.lwjgl.input.Keyboard.
	 * 
	 * @param forward Keycode for forward
	 * @param backward Keycode for backward
	 * @param left Keycode for left
	 * @param right Keycode for right
	 */
	public void move( int forward, int backward, int left, int right)
	{
		movement.zero();
		
		if( Keyboard.isKeyDown(forward))
			movement.add( view );
		if( Keyboard.isKeyDown(backward))
			movement.subtract( view );
		
		if( Keyboard.isKeyDown(left))
		{
			cross.cross(view, up);
			cross.normalize();
			movement.subtract(cross);
		}
		
		if( Keyboard.isKeyDown(right))
		{
			cross.cross(view, up);
			cross.normalize();
			movement.add(cross);
		}
		
		if( movement.squaredlength() > 0.001f )
		{
			movement.normalize();
			movement.scale( 2.0f * Entry.getDelta() );
			//ListenerVel would become movement * speed here.
			pos.add( movement );
		}
	}
	
	/**
	 * Will rotate the camera based on how much the mouse has moved. dX will rotate around the up vector and dY will rotate the view vector around the cross product of up and view.
	 * @param dX How much the mouse moved in the x-axis since last frame
	 * @param dY How much the mouse moved in the y-axis since last frame
	 */
	public void rotate( float dX, float dY )
	{
		if( dX != 0)
			view.rotate(((float)dX) * -0.004f, up.getX(), up.getY(), up.getZ());
		
		if( dY != 0 )
		{
			cross.cross(view,  up);
			cross.normalize();
			
			//rotate around the cross product of view and up (a.k.a. the right vector)
			view.rotate(((float)dY) * 0.004f, cross.getX(), cross.getY(), cross.getZ());
		}
	}

	/**
	 * This lookAt method will shift the position and view by the crossproduct of view and up, and scale it by the shift scalar.
	 * @param shift
	 */
	public void lookAt(float shift)
	{
		cross.cross(view,  up);
		cross.normalize();
		cross.scale(shift);
		gluLookAt(pos.getX()-cross.getX(), pos.getY()-cross.getY(), pos.getZ()-cross.getZ(),
				  pos.getX()-cross.getX() + view.getX(), pos.getY()-cross.getY() + view.getY(), pos.getZ()-cross.getZ() + view.getZ(), 
				  up.getX(), up.getY(), up.getZ());
		
		updateListener();
	}

	/**
	 * Normalizes view, calls gluLookAt and updateListener.
	 */
	public void lookAt()
	{
		view.normalize();
		gluLookAt(pos.getX(), pos.getY(), pos.getZ(), 
				  pos.getX() + view.getX(), pos.getY() + view.getY(), pos.getZ() + view.getZ(), 
				  up.getX(), up.getY(), up.getZ());
		
		updateListener();
	}
}